μλ°μ€ν¬λ¦½νΈ JSON λͺ¨λ μν¬νΈ μμ±μ λν μ¬μΈ΅ λΆμ. μλ‘μ΄ `with { type: 'json' }` ꡬ문, 보μ μ΄μ , κ·Έλ¦¬κ³ λ κΉ¨λνκ³ μμ νλ©° ν¨μ¨μ μΈ μν¬νλ‘μ°λ₯Ό μν΄ κΈ°μ‘΄ λ°©μμ λ체νλ λ°©λ²μ μμ보μΈμ.
μλ°μ€ν¬λ¦½νΈ μν¬νΈ μμ±(Import Attributes): JSON λͺ¨λμ λ‘λνλ νλμ μ΄κ³ μμ ν λ°©λ²
μλ κ° μλ°μ€ν¬λ¦½νΈ κ°λ°μλ€μ JSON νμΌμ λ‘λνλ λ¨μν΄ λ³΄μ΄λ μμ κ³Ό μ¨λ¦ν΄ μμ΅λλ€. JSON(JavaScript Object Notation)μ μΉμμ λ°μ΄ν° κ΅νμ μ¬μ€μ νμ€μ΄μ§λ§, μ΄λ₯Ό μλ°μ€ν¬λ¦½νΈ λͺ¨λμ μννκ² ν΅ν©νλ κ³Όμ μ μμ©κ΅¬ μ½λ, μμλ°©νΈ, μ μ¬μ 보μ μνμΌλ‘ κ°λν μ¬μ μ΄μμ΅λλ€. Node.jsμ λκΈ°μ νμΌ μ½κΈ°λΆν° λΈλΌμ°μ μ μ₯ν©ν `fetch` νΈμΆμ μ΄λ₯΄κΈ°κΉμ§, ν΄κ²°μ± λ€μ λ€μ΄ν°λΈ κΈ°λ₯μ΄λΌκΈ°λ³΄λ€λ μμλ°©νΈμ²λΌ λκ»΄μ‘μ΅λλ€. μ΄μ κ·Έ μλλ λλκ°κ³ μμ΅λλ€.
ECMAScript μΈμ΄λ₯Ό κ΄μ₯νλ μμνμΈ TC39μ μν΄ νμ€νλ νλμ μ΄κ³ μμ νλ©° μ°μν ν΄κ²°μ± , μν¬νΈ μμ±(Import Attributes)μ μΈκ³μ μ€μ κ²μ νμν©λλ€. κ°λ¨νμ§λ§ κ°λ ₯ν `with { type: 'json' }` ꡬ문μΌλ‘ λμ λ μ΄ κΈ°λ₯μ κ°μ₯ νν λΉ(ι)μλ°μ€ν¬λ¦½νΈ μμ°μΈ JSONμ μμμΌλ‘ μ°λ¦¬κ° μ΄λ₯Ό μ²λ¦¬νλ λ°©μμ νμ νκ³ μμ΅λλ€. μ΄ κΈμ μ μΈκ³ κ°λ°μλ€μ μν΄ μν¬νΈ μμ±μ΄ 무μμΈμ§, μ΄λ€ μ€λν λ¬Έμ λ€μ ν΄κ²°νλμ§, κ·Έλ¦¬κ³ λ κΉ¨λνκ³ μμ νλ©° ν¨μ¨μ μΈ μ½λλ₯Ό μμ±νκΈ° μν΄ μ€λλΆν° μ΄λ»κ² μ¬μ©ν μ μλμ§μ λν ν¬κ΄μ μΈ κ°μ΄λλ₯Ό μ 곡ν©λλ€.
κ³Όκ±°μ λ°©μ: μλ°μ€ν¬λ¦½νΈμμ JSONμ λ€λ£¨λ μμ λμ보기
μν¬νΈ μμ±μ μ°μν¨μ μμ ν μ΄ν΄νλ €λ©΄, λ¨Όμ κ·Έκ²μ΄ λ체νκ³ μλ νκ²½μ μ΄ν΄ν΄μΌ ν©λλ€. κ°λ°μλ€μ νκ²½(μλ²μ¬μ΄λ λλ ν΄λΌμ΄μΈνΈμ¬μ΄λ)μ λ°λΌ λ€μν κΈ°μ μ μμ‘΄ν΄ μμΌλ©°, κ° κΈ°μ μλ κ³ μ ν μ₯λ¨μ μ΄ μμμ΅λλ€.
μλ²μ¬μ΄λ(Node.js): `require()`μ `fs`μ μλ
μλ κ° Node.jsμ κΈ°λ³Έμ΄μλ CommonJS λͺ¨λ μμ€ν μμ JSONμ κ°μ Έμ€λ κ²μ λ―Ώμ μ μμ λ§νΌ κ°λ¨νμ΅λλ€:
// CommonJS νμΌμμ (μ: index.js)
const config = require('./config.json');
console.log(config.database.host);
μ΄κ²μ μλ²½νκ² μλνμ΅λλ€. Node.jsλ JSON νμΌμ μλ°μ€ν¬λ¦½νΈ κ°μ²΄λ‘ μλ νμ±νμ΅λλ€. κ·Έλ¬λ μ μΈκ³μ μΌλ‘ ECMAScript λͺ¨λ(ESM)λ‘ μ νλλ©΄μ, μ΄ λκΈ°μ `require()` ν¨μλ νλ μλ°μ€ν¬λ¦½νΈμ λΉλκΈ°μ , μ΅μμ await νΉμ±κ³Ό νΈνλμ§ μκ² λμμ΅λλ€. μ§μ μ μΈ ESM λμλ¬ΌμΈ `import`λ μ΄κΈ°μ JSON λͺ¨λμ μ§μνμ§ μμκ³ , κ°λ°μλ€μ λ μ€λλκ³ μλμ μΈ λ°©λ²μΌλ‘ νκ·ν μλ°μ μμμ΅λλ€:
// ESM νμΌμμ μλμΌλ‘ νμΌ μ½κΈ° (μ: index.mjs)
import fs from 'fs';
import path from 'path';
const configPath = path.resolve('config.json');
const configFile = fs.readFileSync(configPath, 'utf8');
const config = JSON.parse(configFile);
console.log(config.database.host);
μ΄ μ κ·Ό λ°©μμλ λͺ κ°μ§ λ¨μ μ΄ μμ΅λλ€:
- μ₯ν©ν¨: λ¨μΌ μμ μ μν΄ μ¬λ¬ μ€μ μμ©κ΅¬ μ½λκ° νμν©λλ€.
- λκΈ°μ I/O: `fs.readFileSync`λ λΈλ‘νΉ μμ μΌλ‘, λμμ±μ΄ λμ μ ν리μΌμ΄μ μμ μ±λ₯ λ³λͺ© νμμ μΌμΌν¬ μ μμ΅λλ€. λΉλκΈ° λ²μ (`fs.readFile`)μ μ½λ°±μ΄λ Promiseλ‘ μΈν΄ λ λ§μ μμ©κ΅¬ μ½λλ₯Ό μΆκ°ν©λλ€.
- ν΅ν©μ± λΆμ‘±: JSON νμΌμ μλ νμ±μ΄ νμν μΌλ° ν μ€νΈ νμΌλ‘ μ·¨κΈνμ¬ λͺ¨λ μμ€ν κ³Ό λ¨μ λ λλμ μ€λλ€.
ν΄λΌμ΄μΈνΈμ¬μ΄λ(λΈλΌμ°μ ): `fetch` API μμ©κ΅¬ μ½λ
λΈλΌμ°μ μμ κ°λ°μλ€μ μ€λ«λμ μλ²μμ JSON λ°μ΄ν°λ₯Ό λ‘λνκΈ° μν΄ `fetch` APIμ μμ‘΄ν΄ μμ΅λλ€. κ°λ ₯νκ³ μ μ°νμ§λ§, κ°λ¨ν΄μΌ ν κ°μ Έμ€κΈ° μμ μλ λ무 μ₯ν©ν©λλ€.
// κ³ μ μ μΈ fetch ν¨ν΄
let config;
fetch('/config.json')
.then(response => {
if (!response.ok) {
throw new Error('λ€νΈμν¬ μλ΅μ΄ μ¬λ°λ₯΄μ§ μμ΅λλ€');
}
return response.json(); // JSON λ³Έλ¬Έ νμ±
})
.then(data => {
config = data;
console.log(config.api.key);
})
.catch(error => console.error('μ€μ νμΌ fetch μ€λ₯:', error));
μ΄ ν¨ν΄μ ν¨κ³Όμ μ΄κΈ°λ νμ§λ§ λ€μκ³Ό κ°μ λ¨μ μ΄ μμ΅λλ€:
- μμ©κ΅¬ μ½λ: λͺ¨λ JSON λ‘λμλ μ μ¬ν Promise 체μΈ, μλ΅ νμΈ λ° μ€λ₯ μ²λ¦¬κ° νμν©λλ€.
- λΉλκΈ° μ²λ¦¬ μ€λ²ν€λ: `fetch`μ λΉλκΈ°μ νΉμ±μ κ΄λ¦¬νλ κ²μ μ ν리μΌμ΄μ λ‘μ§μ 볡μ‘νκ² λ§λ€ μ μμΌλ©°, λ‘λ© λ¨κ³λ₯Ό μ²λ¦¬νκΈ° μν΄ μ’ μ’ μν κ΄λ¦¬κ° νμν©λλ€.
- μ μ λΆμ λΆκ°: λ°νμ νΈμΆμ΄κΈ° λλ¬Έμ λΉλ λκ΅¬κ° μ΄ μμ‘΄μ±μ μ½κ² λΆμν μ μμ΄ μ΅μ ν κΈ°νλ₯Ό λμΉ μ μμ΅λλ€.
ν κ±Έμ λ: λ¨μΈ(Assertions)μ μ¬μ©ν λμ `import()` (μ΄μ λ²μ )
μ΄λ¬ν λ¬Έμ μ μ μΈμνκ³ TC39 μμνλ μ²μμ μν¬νΈ λ¨μΈ(Import Assertions)μ μ μνμ΅λλ€. μ΄κ²μ κ°λ°μκ° κ°μ Έμ€κΈ°μ λν λ©νλ°μ΄ν°λ₯Ό μ 곡ν μ μλλ‘ νμ¬ ν΄κ²°μ± μ ν₯ν μ€μν λ¨κ³μμ΅λλ€.
// μ΅μ΄μ μν¬νΈ λ¨μΈ μ μ
const configModule = await import('./config.json', { assert: { type: 'json' } });
const config = configModule.default;
μ΄κ²μ μμ²λ κ°μ μ΄μμ΅λλ€. JSON λ‘λ©μ ESM μμ€ν μ ν΅ν©νμ΅λλ€. `assert` μ μ μλ°μ€ν¬λ¦½νΈ μμ§μκ² λ‘λλ 리μμ€κ° μ€μ λ‘ JSON νμΌμΈμ§ νμΈνλΌκ³ μ§μνμ΅λλ€. κ·Έλ¬λ νμ€ν κ³Όμ μμ μ€μν μλ―Έλ‘ μ ꡬλΆμ΄ λνλλ©΄μ μν¬νΈ μμ±μΌλ‘ λ°μ νκ² λμμ΅λλ€.
μν¬νΈ μμ±μ λ±μ₯: μ μΈμ μ΄κ³ μμ ν μ κ·Ό λ°©μ
μμ§ κ΅¬νμλ€μ κ΄λ²μν λ Όμμ νΌλλ°± λμ μν¬νΈ λ¨μΈμ μν¬νΈ μμ±(Import Attributes)μΌλ‘ κ°μ λμμ΅λλ€. ꡬ문μ λ―Έλ¬νκ² λ€λ₯΄μ§λ§, μλ―Έλ‘ μ λ³νλ μ¬μ€ν©λλ€. μ΄κ²μ΄ JSON λͺ¨λμ κ°μ Έμ€λ μλ‘κ³ νμ€νλ λ°©λ²μ λλ€:
μ μ μν¬νΈ:
import config from './config.json' with { type: 'json' };
λμ μν¬νΈ:
const configModule = await import('./config.json', { with: { type: 'json' } });
const config = configModule.default;
`with` ν€μλ: λ¨μν μ΄λ¦ λ³κ²½ κ·Έ μ΄μ
`assert`μμ `with`λ‘μ λ³κ²½μ λ¨μν μΈνμ μΈ κ²μ΄ μλλλ€. μ΄λ λͺ©μ μ κ·Όλ³Έμ μΈ λ³νλ₯Ό λ°μν©λλ€:
- `assert { type: 'json' }`: μ΄ κ΅¬λ¬Έμ λ‘λ ν κ²μ¦μ μλ―Ένμ΅λλ€. μμ§μ λͺ¨λμ κ°μ Έμ¨ λ€μ κ·Έκ²μ΄ λ¨μΈκ³Ό μΌμΉνλμ§ νμΈν©λλ€. μΌμΉνμ§ μμΌλ©΄ μ€λ₯λ₯Ό λ°μμν΅λλ€. μ΄κ²μ μ£Όλ‘ λ³΄μ κ²μ¬μμ΅λλ€.
- `with { type: 'json' }`: μ΄ κ΅¬λ¬Έμ λ‘λ μ μ§μλ¬Έμ μλ―Έν©λλ€. μ΄κ²μ νΈμ€νΈ νκ²½(λΈλΌμ°μ λλ Node.js)μ λͺ¨λμ μ΄λ»κ² λ‘λνκ³ νμ±ν μ§μ λν μ 보λ₯Ό μ²μλΆν° μ 곡ν©λλ€. λ¨μν κ²μ¬κ° μλλΌ μ§μμ λλ€.
μ΄ κ΅¬λΆμ λ§€μ° μ€μν©λλ€. `with` ν€μλλ μλ°μ€ν¬λ¦½νΈ μμ§μκ² "λλ 리μμ€λ₯Ό κ°μ Έμ¬ κ²μ΄λ©°, λ‘λ© κ³Όμ μ μλ΄ν μμ±μ μ 곡νλ€. μ΄ μ 보λ₯Ό μ¬μ©νμ¬ μ¬λ°λ₯Έ λ‘λλ₯Ό μ ννκ³ μ²μλΆν° μ¬λ°λ₯Έ 보μ μ μ± μ μ μ©νλΌ"κ³ λ§νλ κ²κ³Ό κ°μ΅λλ€. μ΄λ λ λμ μ΅μ νμ κ°λ°μμ μμ§ κ°μ λ λͺ νν κ³μ½μ κ°λ₯νκ² ν©λλ€.
μ΄κ²μ΄ μ κ²μ 체μΈμ μΈκ°? 보μμ μ€μμ±
μν¬νΈ μμ±μ κ°μ₯ μ€μν μ΄μ μ 보μμ λλ€. μ΄λ μ격 μ½λ μ€ν(RCE)μΌλ‘ μ΄μ΄μ§ μ μλ MIME νμ νΌλμΌλ‘ μλ €μ§ μ’ λ₯μ 곡격μ λ°©μ§νλλ‘ μ€κ³λμμ΅λλ€.
λͺ¨νΈν μν¬νΈμ RCE μν
μν¬νΈ μμ± μμ΄ λμ μν¬νΈλ₯Ό μ¬μ©νμ¬ μλ²μμ μ€μ νμΌμ λ‘λνλ μλ리μ€λ₯Ό μμν΄ λ³΄μμμ€:
// μ μ¬μ μΌλ‘ μμ νμ§ μμ μν¬νΈ
const { settings } = await import('https://api.example.com/user-settings.json');
`api.example.com`μ μλ²κ° μμλλ©΄ μ΄λ»κ² λ κΉμ? μ μμ μΈ νμμλ `.json` νμ₯μλ κ·Έλλ‘ μ μ§νλ©΄μ `user-settings.json` μλν¬μΈνΈκ° JSON νμΌ λμ μλ°μ€ν¬λ¦½νΈ νμΌμ μ 곡νλλ‘ λ³κ²½ν μ μμ΅λλ€. μλ²λ `Content-Type` ν€λκ° `text/javascript`μΈ μ€ν κ°λ₯ν μ½λλ₯Ό λ°νν κ²μ λλ€.
νμ μ νμΈν λ©μ»€λμ¦μ΄ μλ€λ©΄, μλ°μ€ν¬λ¦½νΈ μμ§μ μλ°μ€ν¬λ¦½νΈ μ½λλ₯Ό λ³΄κ³ μ€ννμ¬ κ³΅κ²©μμκ² μ¬μ©μ μΈμ μ λν μ μ΄κΆμ μ€ μ μμ΅λλ€. μ΄κ²μ μ¬κ°ν 보μ μ·¨μ½μ μ λλ€.
μν¬νΈ μμ±μ΄ μνμ μννλ λ°©λ²
μν¬νΈ μμ±μ μ΄ λ¬Έμ λ₯Ό μ°μνκ² ν΄κ²°ν©λλ€. μμ±κ³Ό ν¨κ» μν¬νΈλ₯Ό μμ±νλ©΄ μμ§κ³Ό μ격ν κ³μ½μ λ§Ίκ² λ©λλ€:
// μμ ν μν¬νΈ
const { settings } = await import('https://api.example.com/user-settings.json' with { type: 'json' });
μ΄μ μΌμ΄λλ μΌμ λ€μκ³Ό κ°μ΅λλ€:
- λΈλΌμ°μ κ° `user-settings.json`μ μμ²ν©λλ€.
- μ΄μ μμλ μλ²λ μλ°μ€ν¬λ¦½νΈ μ½λμ `Content-Type: text/javascript` ν€λλ‘ μλ΅ν©λλ€.
- λΈλΌμ°μ μ λͺ¨λ λ‘λλ μλ΅μ MIME νμ (`text/javascript`)μ΄ μν¬νΈ μμ±μ λͺ μλ μμ νμ (`json`)κ³Ό μΌμΉνμ§ μλ κ²μ νμΈν©λλ€.
- νμΌμ νμ±νκ±°λ μ€ννλ λμ , μμ§μ μ¦μ `TypeError`λ₯Ό λ°μμμΌ μμ μ μ€λ¨νκ³ μ μ± μ½λκ° μ€νλλ κ²μ λ°©μ§ν©λλ€.
μ΄ κ°λ¨ν μΆκ°λ§μΌλ‘ μ μ¬μ μΈ RCE μ·¨μ½μ μ΄ μμ νκ³ μμΈ‘ κ°λ₯ν λ°νμ μ€λ₯λ‘ λ°λλλ€. μ΄λ λ°μ΄ν°κ° λ°μ΄ν°λ‘ μ μ§λκ³ μ°μ°ν μ€ν κ°λ₯ν μ½λλ‘ ν΄μλμ§ μλλ‘ λ³΄μ₯ν©λλ€.
μ€μ©μ μΈ μ¬μ© μ¬λ‘ λ° μ½λ μμ
JSONμ λν μν¬νΈ μμ±μ μ΄λ‘ μ μΈ λ³΄μ κΈ°λ₯μΌ λΏλ§μ΄ μλλλ€. λ€μν μμμμ μΌμμ μΈ κ°λ° μμ μ μΈμ²΄κ³΅νμ κ°μ μ κ°μ Έμ΅λλ€.
1. μ ν리μΌμ΄μ μ€μ λ‘λ
μ΄κ²μ κ³ μ μ μΈ μ¬μ© μ¬λ‘μ λλ€. μλμ μΈ νμΌ I/O λμ , μ΄μ μ€μ μ μ§μ μ μ΄κ³ μ μ μΌλ‘ κ°μ Έμ¬ μ μμ΅λλ€.
νμΌ: `config.json`
{
"database": {
"host": "db.production.example.com",
"port": 5432,
"user": "api_user"
},
"featureFlags": {
"newDashboard": true,
"enableLogging": false
}
}
νμΌ: `database.mjs`
import config from './config.json' with { type: 'json' };
export function getDbHost() {
return config.database.host;
}
console.log(`λ°μ΄ν°λ² μ΄μ€ μ°κ²° μ€: ${getDbHost()}`);
μ΄ μ½λλ κΉ¨λνκ³ μ μΈμ μ΄λ©° μ¬λκ³Ό λΉλ λꡬ λͺ¨λκ° μ΄ν΄νκΈ° μ½μ΅λλ€.
2. κ΅μ ν(i18n) λ°μ΄ν°
λ²μμ κ΄λ¦¬νλ κ²λ μλ²½νκ² λ€μ΄λ§μ΅λλ€. μΈμ΄ λ¬Έμμ΄μ λ³λμ JSON νμΌμ μ μ₯νκ³ νμμ λ°λΌ κ°μ Έμ¬ μ μμ΅λλ€.
νμΌ: `locales/en-US.json`
{
"welcomeMessage": "Hello, welcome to our application!",
"logoutButton": "Log Out"
}
νμΌ: `locales/ko-KR.json`
{
"welcomeMessage": "μλ
νμΈμ, μ ν¬ μ ν리μΌμ΄μ
μ μ€μ κ²μ νμν©λλ€!",
"logoutButton": "λ‘κ·Έμμ"
}
νμΌ: `i18n.mjs`
// κΈ°λ³Έ μΈμ΄λ₯Ό μ μ μΌλ‘ μν¬νΈ
import defaultStrings from './locales/en-US.json' with { type: 'json' };
// μ¬μ©μ μ νΈλμ λ°λΌ λ€λ₯Έ μΈμ΄λ₯Ό λμ μΌλ‘ μν¬νΈ
async function getTranslations(locale) {
if (locale === 'ko-KR') {
const module = await import('./locales/ko-KR.json', { with: { type: 'json' } });
return module.default;
}
return defaultStrings;
}
const userLocale = 'ko-KR';
const strings = await getTranslations(userLocale);
console.log(strings.welcomeMessage); // νκ΅μ΄ λ©μμ§ μΆλ ₯
3. μΉ μ ν리μΌμ΄μ μ μν μ μ λ°μ΄ν° λ‘λ©
κ΅κ° λͺ©λ‘μΌλ‘ λλ‘λ€μ΄ λ©λ΄λ₯Ό μ±μ°κ±°λ μ ν μΉ΄νλ‘κ·Έλ₯Ό νμνλ κ²μ μμν΄ λ³΄μΈμ. μ΄ μ μ λ°μ΄ν°λ JSON νμΌμμ κ΄λ¦¬νκ³ μ»΄ν¬λνΈλ‘ μ§μ κ°μ Έμ¬ μ μμ΅λλ€.
νμΌ: `data/countries.json`
[
{ "code": "US", "name": "United States" },
{ "code": "DE", "name": "Germany" },
{ "code": "JP", "name": "Japan" },
{ "code": "KR", "name": "South Korea" }
]
νμΌ: `CountrySelector.js` (κ°μ μ»΄ν¬λνΈ)
import countries from '../data/countries.json' with { type: 'json' };
export class CountrySelector {
constructor(elementId) {
this.element = document.getElementById(elementId);
this.render();
}
render() {
const options = countries.map(country =>
``
).join('');
this.element.innerHTML = options;
}
}
// μ¬μ©λ²
new CountrySelector('country-dropdown');
λ΄λΆ λμ λ°©μ: νΈμ€νΈ νκ²½μ μν
μν¬νΈ μμ±μ λμμ νΈμ€νΈ νκ²½μ μν΄ μ μλ©λλ€. μ΄λ λΈλΌμ°μ μ Node.js κ°μ μλ²μ¬μ΄λ λ°νμ κ°μ ꡬνμ μ½κ°μ μ°¨μ΄κ° μμμ μλ―Ένμ§λ§, κ²°κ³Όλ μΌκ΄λ©λλ€.
λΈλΌμ°μ μμ
λΈλΌμ°μ 컨ν μ€νΈμμ μ΄ νλ‘μΈμ€λ HTTP λ° MIME νμ κ³Ό κ°μ μΉ νμ€κ³Ό λ°μ νκ² μ°κ²°λμ΄ μμ΅λλ€.
- λΈλΌμ°μ κ° `import data from './data.json' with { type: 'json' }`μ λ§λλ©΄ `./data.json`μ λν HTTP GET μμ²μ μμν©λλ€.
- μλ²λ μμ²μ λ°κ³ JSON μ½ν μΈ λ‘ μλ΅ν΄μΌ ν©λλ€. κ²°μ μ μΌλ‘, μλ²μ HTTP μλ΅μλ `Content-Type: application/json` ν€λκ° ν¬ν¨λμ΄μΌ ν©λλ€.
- λΈλΌμ°μ λ μλ΅μ λ°κ³ `Content-Type` ν€λλ₯Ό κ²μ¬ν©λλ€.
- ν€λμ κ°μ μν¬νΈ μμ±μ μ§μ λ `type`κ³Ό λΉκ΅ν©λλ€.
- μΌμΉνλ©΄ λΈλΌμ°μ λ μλ΅ λ³Έλ¬Έμ JSONμΌλ‘ νμ±νκ³ λͺ¨λ κ°μ²΄λ₯Ό μμ±ν©λλ€.
- μΌμΉνμ§ μμΌλ©΄(μ: μλ²κ° `text/html` λλ `text/javascript`λ₯Ό λ³΄λΈ κ²½μ°), λΈλΌμ°μ λ `TypeError`μ ν¨κ» λͺ¨λ λ‘λλ₯Ό κ±°λΆν©λλ€.
Node.js λ° κΈ°ν λ°νμμμ
λ‘컬 νμΌ μμ€ν μμ μ κ²½μ° Node.jsμ Denoλ MIME νμ μ μ¬μ©νμ§ μμ΅λλ€. λμ , νμΌ νμ₯μμ μν¬νΈ μμ±μ μ‘°ν©μ μμ‘΄νμ¬ νμΌμ μ²λ¦¬νλ λ°©λ²μ κ²°μ ν©λλ€.
- Node.jsμ ESM λ‘λκ° `import config from './config.json' with { type: 'json' }`μ 보면, λ¨Όμ νμΌ κ²½λ‘λ₯Ό μλ³ν©λλ€.
- `with { type: 'json' }` μμ±μ λ΄λΆ JSON λͺ¨λ λ‘λλ₯Ό μ ννλ κ°λ ₯ν μ νΈλ‘ μ¬μ©ν©λλ€.
- JSON λ‘λλ λμ€ν¬μμ νμΌ λ΄μ©μ μ½μ΅λλ€.
- λ΄μ©μ JSONμΌλ‘ νμ±ν©λλ€. νμΌμ μ ν¨νμ§ μμ JSONμ΄ ν¬ν¨λμ΄ μμΌλ©΄ ꡬ문 μ€λ₯κ° λ°μν©λλ€.
- μΌλ°μ μΌλ‘ νμ±λ λ°μ΄ν°λ₯Ό `default` exportλ‘ κ°λ λͺ¨λ κ°μ²΄κ° μμ±λκ³ λ°νλ©λλ€.
μμ±μΌλ‘λΆν°μ μ΄ λͺ μμ μΈ μ§μλ λͺ¨νΈμ±μ νΌν©λλ€. Node.jsλ λ΄μ©μ κ΄κ³μμ΄ νμΌμ μλ°μ€ν¬λ¦½νΈλ‘ μ€ννλ €κ³ μλν΄μλ μ λλ€λ κ²μ λͺ ννκ² μκ² λ©λλ€.
λΈλΌμ°μ λ° λ°νμ μ§μ: νλ‘λμ νκ²½μ μ€λΉλμλκ°?
μλ‘μ΄ μΈμ΄ κΈ°λ₯μ μ±ννλ €λ©΄ λμ νκ²½ μ λ°μ κ±ΈμΉ μ§μμ μ μ€νκ² κ³ λ €ν΄μΌ ν©λλ€. λ€ννλ JSONμ λν μν¬νΈ μμ±μ μλ°μ€ν¬λ¦½νΈ μνκ³ μ λ°μ κ±Έμ³ λΉ λ₯΄κ³ κ΄λ²μνκ² μ±νλμμ΅λλ€. 2023λ νλ° κΈ°μ€μΌλ‘ μ΅μ νκ²½μμμ μ§μμ νλ₯ν©λλ€.
- Google Chrome / Chromium μμ§ (Edge, Opera): λ²μ 117λΆν° μ§μλ©λλ€.
- Mozilla Firefox: λ²μ 121λΆν° μ§μλ©λλ€.
- Safari (WebKit): λ²μ 17.2λΆν° μ§μλ©λλ€.
- Node.js: λ²μ 21.0λΆν° μμ ν μ§μλ©λλ€. μ΄μ λ²μ (μ: v18.19.0+, v20.10.0+)μμλ `--experimental-import-attributes` νλκ·Έ λ€μμ μ¬μ©ν μ μμμ΅λλ€.
- Deno: μ§λ³΄μ μΈ λ°νμμΌλ‘μ Denoλ λ²μ 1.34λΆν° μ΄ κΈ°λ₯(λ¨μΈμμ μ§ν)μ μ§μν΄ μμ΅λλ€.
- Bun: λ²μ 1.0λΆν° μ§μλ©λλ€.
μ΄μ λ²μ μ λΈλΌμ°μ λ Node.jsλ₯Ό μ§μν΄μΌ νλ νλ‘μ νΈμ κ²½μ°, Vite, Webpack(μ μ ν λ‘λ μ¬μ© μ), Babel(λ³ν νλ¬κ·ΈμΈ μ¬μ© μ)κ³Ό κ°μ μ΅μ λΉλ λꡬ λ° λ²λ€λ¬κ° μ ꡬ문μ νΈν κ°λ₯ν νμμΌλ‘ νΈλμ€νμΌν μ μμ΄ μ€λλ μλ μ΅μ μ½λλ₯Ό μμ±ν μ μμ΅λλ€.
JSONμ λμ΄: μν¬νΈ μμ±μ λ―Έλ
JSONμ΄ μ²« λ²μ§Έμ΄μ κ°μ₯ λλλ¬μ§ μ¬μ© μ¬λ‘μ΄μ§λ§, `with` ꡬ문μ νμ₯ κ°λ₯νλλ‘ μ€κ³λμμ΅λλ€. μ΄λ λͺ¨λ μν¬νΈμ λ©νλ°μ΄ν°λ₯Ό 첨λΆνλ μΌλ°μ μΈ λ©μ»€λμ¦μ μ 곡νμ¬, λ€λ₯Έ μ νμ λΉ-μλ°μ€ν¬λ¦½νΈ 리μμ€κ° ES λͺ¨λ μμ€ν μ ν΅ν©λ μ μλ κΈΈμ μ΄μ΄μ€λλ€.
CSS λͺ¨λ μ€ν¬λ¦½νΈ
κ°κΉμ΄ λ―Έλμ λμ¬ λ€μ μ£Όμ κΈ°λ₯μ CSS λͺ¨λ μ€ν¬λ¦½νΈμ λλ€. μ΄ μ μμ κ°λ°μκ° CSS μ€νμΌμνΈλ₯Ό λͺ¨λλ‘ μ§μ κ°μ Έμ¬ μ μκ² ν©λλ€:
import sheet from './styles.css' with { type: 'css' };
document.adoptedStyleSheets = [sheet];
CSS νμΌμ΄ μ΄λ° λ°©μμΌλ‘ μν¬νΈλλ©΄, `CSSStyleSheet` κ°μ²΄λ‘ νμ±λμ΄ νλ‘κ·Έλλ° λ°©μμΌλ‘ λ¬Έμλ μλμ° DOMμ μ μ©λ μ μμ΅λλ€. μ΄λ μΉ μ»΄ν¬λνΈμ λμ μ€νμΌλ§μ μμ΄ ν° λμ½μ΄λ©°, `